Skip to content

Connector & Engine adjustments for asset scale precision loss leftovers - #199

Merged
gakonst merged 12 commits into
masterfrom
gakonst-precision-loss
Aug 15, 2019
Merged

Connector & Engine adjustments for asset scale precision loss leftovers#199
gakonst merged 12 commits into
masterfrom
gakonst-precision-loss

Conversation

@gakonst

@gakonst gakonst commented Aug 8, 2019

Copy link
Copy Markdown
Collaborator

Closes #185

@gakonst
gakonst requested a review from emschwartz as a code owner August 8, 2019 10:45
@gakonst
gakonst changed the base branch from master to gakonst-asset-scale August 8, 2019 10:45
@gakonst gakonst changed the title Connector & Engine adjustments for precision loss leftovers Connector & Engine adjustments for asset scale precision loss leftovers Aug 8, 2019
Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
Comment thread crates/interledger-settlement-engines/src/stores/redis_ethereum_ledger/store.rs Outdated
@gakonst
gakonst force-pushed the gakonst-asset-scale branch from 4927f5d to d227963 Compare August 8, 2019 15:57
@gakonst
gakonst force-pushed the gakonst-precision-loss branch from 09c89e6 to 0b8ee3f Compare August 9, 2019 09:24
Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
@gakonst

gakonst commented Aug 9, 2019

Copy link
Copy Markdown
Collaborator Author

Adding a type_length_limit check at the main.rs file does not seem to allow tests to build:

cargo build --all-features --all-targets
   Compiling interledger-settlement-engines v0.1.0 (/Users/gakonst/projects/xpring-contract/interledger-rs/crates/interledger-settlement-engines)
error: reached the type-length limit while instantiating `<std::vec::IntoIter<ethereum_typ...]]], std::result::Result<(), !>>`
     |
     = note: consider adding a `#![type_length_limit="1689024"]` attribute to your crate

error: aborting due to previous error

error: Could not compile `interledger-settlement-engines`.
warning: build failed, waiting for other jobs to finish...
error: reached the type-length limit while instantiating `<std::vec::IntoIter<ethereum_typ...]]], std::result::Result<(), !>>`
     |
     = note: consider adding a `#![type_length_limit="1689024"]` attribute to your crate

error: aborting due to previous error

error: Could not compile `interledger-settlement-engines`.
warning: build failed, waiting for other jobs to finish...
error: reached the type-length limit while instantiating `<std::vec::IntoIter<ethereum_typ...]]], std::result::Result<(), !>>`
     |
     = note: consider adding a `#![type_length_limit="1689024"]` attribute to your crate

error: aborting due to previous error

error: Could not compile `interledger-settlement-engines`.

Error is pretty opaque, does not indicate where we need to adjust things

Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
Comment thread crates/interledger-settlement-engines/src/stores/mod.rs Outdated
Comment thread crates/interledger-settlement-engines/src/stores/redis_ethereum_ledger/store.rs Outdated
Comment thread crates/interledger-settlement-engines/src/stores/mod.rs Outdated
Comment thread crates/interledger-settlement-engines/src/stores/mod.rs Outdated
@gakonst
gakonst force-pushed the gakonst-precision-loss branch from 05dce10 to 055d365 Compare August 9, 2019 18:01
@gakonst
gakonst changed the base branch from gakonst-asset-scale to master August 9, 2019 18:01
@gakonst

gakonst commented Aug 12, 2019

Copy link
Copy Markdown
Collaborator Author

Maybe related: rust-lang/rust#54540 (comment)

Turns out boxing the futures instead of returning Either's does the trick. Not sure how I feel about it, but I cannot find another workaround.

In linkmauve/tokio-socks5@a532d63, they allude how they use Boxes to avoid long compilation times because the nested futures really make the compiler tired.

@gakonst
gakonst force-pushed the gakonst-precision-loss branch from 488677e to 88dc75d Compare August 12, 2019 14:03
@gakonst
gakonst requested a review from emschwartz August 12, 2019 14:04
@gakonst

gakonst commented Aug 12, 2019

Copy link
Copy Markdown
Collaborator Author

@emschwartz this should be good for review again. Curious what you think on Box'ing the futures. Seems like there's no good alternative.

@emschwartz emschwartz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I still prefer load_uncredited_settlement_amount and save_uncredited_settlement_amount over load/pop leftovers

Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
Comment thread crates/interledger-settlement-engines/src/engines/ethereum_ledger/eth_engine.rs Outdated
) -> Box<dyn Future<Item = (), Error = ()> + Send> {
trace!("Saving leftovers {:?} {:?}", account_id, leftovers);
let mut pipe = redis::pipe();
pipe.set(format!("leftovers:{}", account_id), leftovers.to_string())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be adding to that amount rather than overwriting what's there

@gakonst gakonst Aug 12, 2019

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's correct since there may be a race condition. However, the amount being written is a BigNumber, we cannot do arithmetic with these in the store, unless we store them as numbers in redis.

What should we do if the connector replies with leftovers greater than u64? Alternatively, we should explore some way to perform atomic threadsafe operations on Redis where we pull some value, operate on it and write back to it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of a janky solution but we could use a list of strings in Redis. When you want to save an amount, you would add another number to the list. When you want to get the leftovers, you would load the whole list (and empty it out -- although this creates a possibility that information would get lost if the SE crashes right then) and sum up the numbers parsed from the strings using the big integer library.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the list of strings idea. When we fetch the leftovers, I also think we can mitigate any risk of the SE crashing:

  1. Generate a new idempotency key locally
  2. Run a pipeline or Lua script that:
    a. Gets the uncredited list (and returns right away if it's empty)
    b. Sets new key(s) with the idempotency key, its expiration, and the list of amounts it corresponds to
    b. Empties the uncredited list
    d. Returns the list of amounts corresponding to the idempotency key
  3. Then, the BigNumber logic locally can sum up the list of amounts for the incoming settlement request. If it crashes and comes back online, it can safely lookup all unexpired idempotency keys, and add up their list of amounts locally to compute the total amount that should be sent in each of the incoming settlement requests.
  4. If that idempotency key expires/the request fails after a few retries, we can atomically append its list of amounts to the list of uncredited amounts and delete the idempotency key.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of strings logic has been added, thanks for the suggestion @emschwartz.

Ok(())
})
})
let amount = BigUint::from(100u64);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should use numbers that are larger than u64, just to ensure it does exactly what we expect

@gakonst
gakonst requested a review from emschwartz August 13, 2019 17:13
uncredited_settlement_amount
);
let mut pipe = redis::pipe();
pipe.lpush(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth including a comment recording why these amounts are stored as a list

let mut pipe = redis::pipe();
// Loads the value and resets it to 0
pipe.lrange(
format!("uncredited_settlement_amount:{}", account_id),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this key be prefixed with the SE type like the other keys?

@gakonst
gakonst force-pushed the gakonst-precision-loss branch from db28493 to 0d5a477 Compare August 15, 2019 16:50
@gakonst
gakonst merged commit 7cfa6bf into master Aug 15, 2019
@gakonst
gakonst deleted the gakonst-precision-loss branch August 15, 2019 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Settlement Engine Asset Scale

3 participants